package org.smartboot.flow.core.attribute;

import org.smartboot.flow.core.exception.FlowException;
import org.smartboot.flow.core.parser.DefaultObjectCreator;
import org.smartboot.flow.core.parser.ObjectCreator;
import org.smartboot.flow.core.util.AssertUtil;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;

/**
 * @author qinluo
 * @date 2022-11-28 20:41:17
 * @since 1.0.0
 */
public class AttributeValueResolver {

    private static final AttributeValueResolver INSTANCE = new AttributeValueResolver();
    private ObjectCreator objectCreator = DefaultObjectCreator.getInstance();

    private AttributeValueResolver() {
    }

    public AttributeValueResolver(ObjectCreator objectCreator) {
        this.objectCreator = objectCreator;
    }

    public static AttributeValueResolver getInstance() {
        return INSTANCE;
    }

    public Object resolve(Attributes attribute, Object value) {
        if (value == null) {
            return null;
        }

        if (attribute.accept(value)) {
            return value;
        }

        Class<?> accepted = attribute.accept;
        // Must as string
        String strValue = String.valueOf(value);

        // For string
        if (attribute.accept(strValue)) {
            return strValue;
        }

        // For boolean
        if (accepted == Boolean.class || accepted == boolean.class) {
            return Boolean.parseBoolean(strValue);
        }

        // For numbers
        if (Number.class.isAssignableFrom(accepted) || accepted.isPrimitive()) {
            Double numeric = Double.valueOf(strValue);

            if (accepted == Long.class || accepted == long.class) {
                return numeric.longValue();
            }

            if (accepted == Double.class || accepted == double.class) {
                return numeric;
            }

            if (accepted == Integer.class || accepted == int.class) {
                return numeric.intValue();
            }

            if (accepted == Float.class || accepted == float.class) {
                return numeric.floatValue();
            }

            if (accepted == Short.class || accepted == short.class) {
                return numeric.shortValue();
            }

            if (accepted == Byte.class || accepted == byte.class) {
                return numeric.byteValue();
            }

            if (accepted == Character.class || accepted == char.class) {
                return (char)numeric.intValue();
            }

            if (accepted == BigDecimal.class) {
                return new BigDecimal(strValue);
            }

            AssertUtil.shouldNotReachHere();
        }

        // For string list.
        if (accepted == List.class) {
            return Arrays.asList(strValue.split(","));
        }

        // for classname.
        try {
            return objectCreator.create(strValue, attribute.accept,true);
        } catch (Exception ignored) {
            // Maybe not a class.
        }

        throw new FlowException("Can't not resolve attribute [" + attribute.getName() + "] value with " + value);
    }
}
